// 版权所有2012 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// 此文件实现常用的类型谓词。

package types

import (
	"go/token"
)

// isNamed报告typ是否有名称。
// isNamed可以使用未完全设置的类型调用。
func isNamed(typ Type) bool {
	switch typ.(type) {
	case *Basic, *Named, *_TypeParam, *instance:
		return true
	}
	return false
}

// isGeneric报告类型是否为泛型、未实例化类型（泛型）
// 签名不包括在内）。
func isGeneric(typ Type) bool {
	// 参数化类型只有在没有实例化的情况下才会实例化。
	named, _ := typ.(*Named)
	return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil
}

func is(typ Type, what BasicInfo) bool {
	switch t := optype(typ).(type) {
	case *Basic:
		return t.info&what != 0
	case *_Sum:
		return t.is(func(typ Type) bool { return is(typ, what) })
	}
	return false
}

func isBoolean(typ Type) bool  { return is(typ, IsBoolean) }
func isInteger(typ Type) bool  { return is(typ, IsInteger) }
func isUnsigned(typ Type) bool { return is(typ, IsUnsigned) }
func isFloat(typ Type) bool    { return is(typ, IsFloat) }
func isComplex(typ Type) bool  { return is(typ, IsComplex) }
func isNumeric(typ Type) bool  { return is(typ, IsNumeric) }
func isString(typ Type) bool   { return is(typ, IsString) }

// 请注意，如果typ是一个类型参数，则isInteger（typ）| | isFloat（typ）不会
// 生成预期结果，因为类型列表包含两个整数
// 浮点类型既不是（全部）整数，也不是（全部）浮点。
// 改用isIntegerOrFloat。
func isIntegerOrFloat(typ Type) bool { return is(typ, IsInteger|IsFloat) }

// isNumericOrString相当于isNumeric（典型）| | isString（典型）的isIntegerOrFloat。
func isNumericOrString(typ Type) bool { return is(typ, IsNumeric|IsString) }

// isTyped报告是否键入了类型；i、 例如，不是非类型的
// 常量或布尔值。可以使用以下类型调用isTyped
// 未完全设置。
func isTyped(typ Type) bool {
	// isTyped是用不完全相同的类型调用的
	// 设置不能调用asBasic（）！
	// 始终键入*命名或*实例类型，因此
	// 我们只需要检查我们是否有一个真实的*基本
	// 类型
	t, _ := typ.(*Basic)
	return t == nil || t.info&IsUntyped == 0
}

// isUntyped（典型）与相同！isTyped（典型）。
func isUntyped(typ Type) bool {
	return !isTyped(typ)
}

func isOrdered(typ Type) bool { return is(typ, IsOrdered) }

func isConstType(typ Type) bool {
	// 类型参数永远不是常量类型。
	t, _ := under(typ).(*Basic)
	return t != nil && t.info&IsConstType != 0
}

// IsInterface报告typ是否为接口类型。
func IsInterface(typ Type) bool {
	return asInterface(typ) != nil
}

// 可比报告T型值是否可比。
func Comparable(T Type) bool {
	return comparable(T, nil)
}

func comparable(T Type, seen map[Type]bool) bool {
	if seen[T] {
		return true
	}
	if seen == nil {
		seen = make(map[Type]bool)
	}
	seen[T] = true

	// 如果T是不受任何类型约束的类型参数
	// 列表（即，其基础类型为顶部类型），
	// 如果T具有==方法，则T是可比较的。否则
	// 基础类型为“wins”。例如
	// None
	// 接口{可比较；类型[]字节}
	// None
	// 不可比较，因为[]字节不可比较。
	if t := asTypeParam(T); t != nil && optype(t) == theTop {
		return t.Bound()._IsComparable()
	}

	switch t := optype(T).(type) {
	case *Basic:
		// 假设无效类型具有可比性
		// 避免后续错误
		return t.kind != UntypedNil
	case *Pointer, *Interface, *Chan:
		return true
	case *Struct:
		for _, f := range t.fields {
			if !comparable(f.typ, seen) {
				return false
			}
		}
		return true
	case *Array:
		return comparable(t.elem, seen)
	case *_Sum:
		pred := func(t Type) bool {
			return comparable(t, seen)
		}
		return t.is(pred)
	case *_TypeParam:
		return t.Bound()._IsComparable()
	}
	return false
}

// hasNil报告类型是否包含nil值。
func hasNil(typ Type) bool {
	switch t := optype(typ).(type) {
	case *Basic:
		return t.kind == UnsafePointer
	case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
		return true
	case *_Sum:
		return t.is(hasNil)
	}
	return false
}

// 相同报告x和y是否为相同类型。
// 签名类型的接收者将被忽略。
func (check *Checker) identical(x, y Type) bool {
	return check.identical0(x, y, true, nil)
}

// identicalIgnoreTags报告如果忽略标记，x和y是否为相同的类型。
// 签名类型的接收者将被忽略。
func (check *Checker) identicalIgnoreTags(x, y Type) bool {
	return check.identical0(x, y, false, nil)
}

// ifacePair是一组接口类型对中的节点，用于比较标识。
type ifacePair struct {
	x, y *Interface
	prev *ifacePair
}

func (p *ifacePair) identical(q *ifacePair) bool {
	return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
}

// 对于此代码的更改，应对unifier.nify进行相应的更改。
func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
	// 类型必须展开以进行比较
	x = expandf(x)
	y = expandf(y)

	if x == y {
		return true
	}

	switch x := x.(type) {
	case *Basic:
		// 除了符文和字节之外，基本类型都是单例
		// 别名，因此我们不能仅依赖于x==y检查
		// 在上面另请参见TypeName.IsAlias中的注释。
		if y, ok := y.(*Basic); ok {
			return x.kind == y.kind
		}

	case *Array:
		// 如果两种数组类型具有相同的元素类型，则它们是相同的
		// 和相同的数组长度。
		if y, ok := y.(*Array); ok {
			// 如果由于某些错误，一个或两个数组长度未知（<0），
			// 假设它们是相同的，以避免虚假的后续错误。
			return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p)
		}

	case *Slice:
		// 如果两个切片类型具有相同的元素类型，则它们是相同的。
		if y, ok := y.(*Slice); ok {
			return check.identical0(x.elem, y.elem, cmpTags, p)
		}

	case *Struct:
		// 如果两个结构类型具有相同的字段序列，则它们是相同的，
		// 如果相应的字段具有相同的名称和相同的类型，
		// 和相同的标签。两个嵌入字段被视为具有相同的属性
		// 名称来自不同包的小写字段名称总是不同的。
		if y, ok := y.(*Struct); ok {
			if x.NumFields() == y.NumFields() {
				for i, f := range x.fields {
					g := y.fields[i]
					if f.embedded != g.embedded ||
						cmpTags && x.Tag(i) != y.Tag(i) ||
						!f.sameId(g.pkg, g.name) ||
						!check.identical0(f.typ, g.typ, cmpTags, p) {
						return false
					}
				}
				return true
			}
		}

	case *Pointer:
		// 如果两个指针类型具有相同的基类型，则它们是相同的。
		if y, ok := y.(*Pointer); ok {
			return check.identical0(x.base, y.base, cmpTags, p)
		}

	case *Tuple:
		// 如果两个元组具有相同数量的元素，则它们是相同的
		// 相应的元素具有相同的类型。
		if y, ok := y.(*Tuple); ok {
			if x.Len() == y.Len() {
				if x != nil {
					for i, v := range x.vars {
						w := y.vars[i]
						if !check.identical0(v.typ, w.typ, cmpTags, p) {
							return false
						}
					}
				}
				return true
			}
		}

	case *Signature:
		// 如果两个函数类型具有相同数量的参数，则它们是相同的
		// 和结果值，对应的参数和结果类型相同，
		// 或者两个函数都是可变的，或者两者都不是。参数与结果
		// 名称不需要匹配。
		// 泛型函数还必须具有匹配的类型参数列表，但对于
		// 参数名。
		if y, ok := y.(*Signature); ok {
			return x.variadic == y.variadic &&
				check.identicalTParams(x.tparams, y.tparams, cmpTags, p) &&
				check.identical0(x.params, y.params, cmpTags, p) &&
				check.identical0(x.results, y.results, cmpTags, p)
		}

	case *_Sum:
		// 如果两个总和类型包含相同的类型，则它们是相同的。
		// （总和类型始终至少包含两种类型。此外
		// 总和类型中的类型集（列表）由唯一
		// 类型-每个类型只显示一次。因此，有两种求和类型
		// 必须包含相同数量的类型才能有机会
		// 平等。
		if y, ok := y.(*_Sum); ok && len(x.types) == len(y.types) {
			// x.types中的每个类型都必须是y.types。
			// 二次算法，但现在可能已经足够好了。
			// TODO（gri）我们需要所有类型的快速类型ID/哈希。
		L:
			for _, x := range x.types {
				for _, y := range y.types {
					if Identical(x, y) {
						continue L // x是y类型
					}
				}
				return false // x不是y类型
			}
			return true
		}

	case *Interface:
		// 如果两个接口类型具有相同的方法集，则它们是相同的
		// 相同的名称和相同的函数类型。来自的小写方法名称
		// 不同的包装总是不同的。方法的顺序是不相关的。
		if y, ok := y.(*Interface); ok {
			// 如果通过外部API入口点（间接）调用identical0
			// （如相同、相同的木质素标签等），检查为零。但是在
			// 在这种情况下，接口应该是完整的，并且是延迟完成的
			// 这是不需要的。
			if check != nil {
				check.completeInterface(token.NoPos, x)
				check.completeInterface(token.NoPos, y)
			}
			a := x.allMethods
			b := y.allMethods
			if len(a) == len(b) {
				// 接口类型是唯一可以发生循环的类型
				// 不是通过命名类型“终止”的；这样的循环
				// 只能通过以下方法参数类型创建
				// 匿名接口（直接或间接）嵌入
				// 当前接口。例子：
				// None
				// T型接口{
				// m（）接口{T}
				// }
				// None
				// 如果比较两个这样的（名称不同的）接口，
				// 如果未检测到循环，则会发生无休止的递归。
				// None
				// 如果之前比较过x和y，它们必须相等
				// （如果不是，递归就会停止）；
				// 在ifacePair堆栈中搜索同一对。
				// None
				// 这是一个二次算法，但实际上这些堆栈
				// 非常短（受界面嵌套深度限制
				// 通过参数类型重复出现的类型声明，这是一个非常复杂的问题
				// 罕见事件）。另一种实现可能使用
				// “已访问”地图，但总体效率可能较低。
				q := &ifacePair{x, y, p}
				for p != nil {
					if p.identical(q) {
						return true // 同一对之前进行过比较
					}
					p = p.prev
				}
				if debug {
					assertSortedMethods(a)
					assertSortedMethods(b)
				}
				for i, f := range a {
					g := b[i]
					if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) {
						return false
					}
				}
				return true
			}
		}

	case *Map:
		// 如果两个贴图类型具有相同的键和值类型，则它们是相同的。
		if y, ok := y.(*Map); ok {
			return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p)
		}

	case *Chan:
		// 如果两个通道类型具有相同的值类型，则它们是相同的
		// 方向是一样的。
		if y, ok := y.(*Chan); ok {
			return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p)
		}

	case *Named:
		// 如果两个命名类型的类型名源自
		// 在同一类型声明中。
		if y, ok := y.(*Named); ok {
			// TODO（gri）为什么x==y不够？如果是的话，
			// 我们可以在这里返回false，因为x==y
			// 在该函数的最开始处被捕获。
			return x.obj == y.obj
		}

	case *_TypeParam:
		// 无需执行任何操作（x和y相等被捕获在此函数的开头）

	// 案例*实例：
	// 无法访问，因为类型已展开

	case *bottom, *top:
		// 要么两种类型都是底部，要么两种类型都是顶部
		// 如果初始x==y检查将捕获它们。否则
		// 它们并不完全相同。

	case nil:
		// 避免在无类型的情况下发生崩溃

	default:
		unreachable()
	}

	return false
}

func (check *Checker) identicalTParams(x, y []*TypeName, cmpTags bool, p *ifacePair) bool {
	if len(x) != len(y) {
		return false
	}
	for i, x := range x {
		y := y[i]
		if !check.identical0(x.typ.(*_TypeParam).bound, y.typ.(*_TypeParam).bound, cmpTags, p) {
			return false
		}
	}
	return true
}

// 对于“非类型化”类型，Default返回默认的“类型化”类型；
// 它返回所有其他类型的传入类型。默认类型
// 对于非类型化，nil是非类型化的nil。
// None
func Default(typ Type) Type {
	if t, ok := typ.(*Basic); ok {
		switch t.kind {
		case UntypedBool:
			return Typ[Bool]
		case UntypedInt:
			return Typ[Int]
		case UntypedRune:
			return universeRune // 使用“符文”名称
		case UntypedFloat:
			return Typ[Float64]
		case UntypedComplex:
			return Typ[Complex128]
		case UntypedString:
			return Typ[String]
		}
	}
	return typ
}
